package cn.dev.mybatis.core;

import cn.dev.mybatis.annotation.Mapper;
import cn.dev.mybatis.annotation.MapperScan;
import cn.dev.mybatis.exception.MybatisException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 1. 获取扫描路径
        HashSet<String> basePaths = new HashSet<>();
        Map<String, Object> attributes = metadata.getAnnotationAttributes(MapperScan.class.getName());
        Collections.addAll(basePaths, (String[]) Objects.requireNonNull(attributes).get("basePackages"));
        if (basePaths.isEmpty()) {
            basePaths.add(ClassUtils.getPackageName(metadata.getClassName()));
        }

        // 扫描
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider() {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return true;
            }
        };
        scanner.setEnvironment(environment);
        scanner.setResourceLoader(resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class));

        // 注册
        for (String path : basePaths) {
            scanner.findCandidateComponents(path).forEach(beanDefinition -> {
                if (beanDefinition instanceof AnnotatedBeanDefinition definition) {
                    registerBeanDefinition(definition, registry);
                }
            });
        }
    }

    private void registerBeanDefinition(AnnotatedBeanDefinition definition, BeanDefinitionRegistry registry) {
        String className = definition.getMetadata().getClassName();
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class);
        builder.addPropertyValue("type", className);
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        try {
            beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, Class.forName(className));
            BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(beanDefinition, className), registry);
        } catch (ClassNotFoundException e) {
            throw new MybatisException(e);
        }
    }


    private ResourceLoader resourceLoader;

    private Environment environment;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}
